﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AlgorithmLibrary
{
    public class CanLibrary
    {

        public delegate void CallbackFunction (uint Tsjw, uint Tbs1, uint Tbs2, uint BRP, uint CiaVal);

        //如果这份代码需要放到MCU中，还需要测试最大值，目前10240肯定比实际需要的大
        const int GROUP_NUM = 10240;//256  //缓冲区的最大组数

        //can波特率最小值
        const int CAN_BAUD_MIN_VAL = (3000);
        //can波特率最大值
        const int CAN_BAUD_MAX_VAL = (1000000);
        //测试步进值
        const int TEST_STEP_VALUE = (1000);

        //重新同步跳跃时间单元的最大最小值
        const int TSJW_MIN_VAL = (1);
        const int TSJW_MAX_VAL = (4);
        //时间段1的时间单元
        const int TBS1_MIN_VAL = (1);
        const int TBS1_MAX_VAL = (16);
        //时间段2的时间单元
        const int TBS2_MIN_VAL = (1);
        const int TBS2_MAX_VAL = (8);
        //分频系数
        const int BRP_MIN_VAL = (1);
        const int BRP_MAX_VAL = (1024);
        //TSJW + TBS1 + TBS2 的最大值
        const int TSJW_TBS1_TBS2_ADD_MAX = (4 + 16 + 8);
        //TSJW + TBS1 + TBS2 的最小值
        const int TSJW_TBS1_TBS2_ADD_MIN = (1 + 1 + 1);


        //CAN控制器时钟，48M=48000000
        const UInt64 CAN_CLOCK_FREQUENCY = (48000000ul);


        //CIA采样点最小值
        const int CIA_MIN_VAL = (850);
        //CIA采样点最大值
        const int CIA_MAX_VAL = (900);
        //波特率误差放大倍数
        const int ERROR_MAGNIFICATION = ((1000 * 100));

        //放大1000倍
        const int BAUD_PARAM_ERROR = (5 * ERROR_MAGNIFICATION / 1000);//0.005 --> 0.5%
                                                                      //放大1000倍
        const int CIA_PARAM_ERROR = 10;//0.01 --> 1%




        //计算波特率
        //T=Tsjw+Tbs1+Tbs2
        UInt64 CALC_BAUD(uint T, uint Brp) {
            return (CAN_CLOCK_FREQUENCY / ((T) * (Brp)));

        }
        //计算T值
        UInt64 CALC_T_VAL(uint actualBaudVal, uint Brp) { return (CAN_CLOCK_FREQUENCY / ((actualBaudVal) * (Brp))); }
        //采样点计算，(TBS1+1) / (1+TBS1+TBS2)  或是  (Tsjw+TBS1) / (Tsjw+TBS1+TBS2)  
        //根据CIA推荐采样点，最好设置在85%~90%。放大1000倍就是850--900
        //Tsjw-->x,TBS1-->y,TBS2-->z
        UInt64 CALC_CIA_VAL(uint Tsjw, uint Tbs1, uint Tbs2) { return (((Tsjw) + (Tbs1)) * 1000 / ((Tsjw) + (Tbs1) + (Tbs2))); }
        //计算波特率误差，算出的波特率/实际波特率 * 1000，求绝对值
        //#define CALC_BAUD_ERROR(calcBaudVal,actualBaudVal) abs( ERROR_MAGNIFICATION - (calcBaudVal*ERROR_MAGNIFICATION)/(actualBaudVal) )
        UInt64 CALC_BAUD_ERROR(uint calcBaudVal, uint actualBaudVal) { return (UInt64)Math.Abs(ERROR_MAGNIFICATION - ((calcBaudVal / actualBaudVal) * ERROR_MAGNIFICATION)); }

        class CAN_BAUD_PARAM {
            //重新同步跳跃时间单元
            public uint Tsjw;
            //时间段1的时间单元
            public uint BS1;
            //时间段2的时间单元
            public uint BS2;
            //分频系数
            public uint BRP;
            //波特率误差，即：算出的波特率/实际波特率 * 100%
            public uint BaudErr;
            //其实不需要这个误差，CiaVal在85%-90%之间即可
            //unsigned short CiaErr;
            //CiaVal在85%-90%之间
            public uint CiaVal;
        }
        class CAN_PARAM {
            public CAN_BAUD_PARAM[] Param = new CAN_BAUD_PARAM[GROUP_NUM];
            public uint index;
        }
        class CIAVALRECORD {
            public uint CiaErr;
            public uint Record;
        }



        uint CalcBaudError(uint calcBaudVal, uint actualBaudVal)
        {
            double err = calcBaudVal;
            err = err * ERROR_MAGNIFICATION;
            err = err / actualBaudVal;
            err = Math.Abs(ERROR_MAGNIFICATION - err);
            return (uint) err;
        }


        //===============================================================
        //功能说明：计算CAN波特率，误差限制在1%内，CIA值限制在85%-90%的范围内
        //传入参数：actualBaudVal，实际波特率
        //			callbackFun，回调函数，用于输出寄存器值
        //===============================================================

        UInt64 CalcCANBaud(uint actualBaudVal)
        {
            CAN_PARAM CParam = new CAN_PARAM();
            CAN_BAUD_PARAM temp = new CAN_BAUD_PARAM();
            //CIAVALRECORD CiaValRecord;
            UInt64 result = 0;
            //初始化相关参数
            CParam.index = 0;
            temp.BaudErr = 0;
            temp.BRP = 0;
            temp.BS1 = 0;
            temp.BS2 = 0;
            temp.CiaVal = 0;
            temp.Tsjw = 0;
            for (int index = 0; index < GROUP_NUM; index++)
            {
                CParam.Param[index] = new CAN_BAUD_PARAM();
                CParam.Param[index].Tsjw = 0;
                CParam.Param[index].BS1 = 0;
                CParam.Param[index].BS2 = 0;
                CParam.Param[index].BaudErr = 0;
                //CParam.Param[index].CiaErr = 0;
                CParam.Param[index].CiaVal = 0;
            }

            for (int BRP = BRP_MIN_VAL; BRP <= BRP_MAX_VAL; BRP++)
            {
                UInt64 T = CALC_T_VAL(actualBaudVal, (uint)BRP);
                //如果结果比最大值还大，即不需要往下执行
                if (T > TSJW_TBS1_TBS2_ADD_MAX) continue;
                //如果结果比最小值还小，即不需要往下执行
                if (T < TSJW_TBS1_TBS2_ADD_MIN) continue;

                //反推波特率
                UInt64 calcBaudVal = CALC_BAUD((uint)T, (uint)BRP);
                //计算波特率误差
                //unsigned int baudErr = CALC_BAUD_ERROR(calcBaudVal, actualBaudVal);
                UInt64 baudErr = CalcBaudError((uint)calcBaudVal, actualBaudVal);
                //如果波特率的误差过大，需要重新计算
                if (baudErr > BAUD_PARAM_ERROR) continue;

                for (uint Tsjw = TSJW_MIN_VAL; Tsjw <= TSJW_MAX_VAL; Tsjw++)
                {
                    for (uint Tbs2 = TBS2_MIN_VAL; Tbs2 <= TBS2_MAX_VAL; Tbs2++)
                    {

                        uint Tbs1 = (uint)(T - Tsjw - Tbs2);

                        if (Tsjw > TSJW_MAX_VAL) continue;
                        if (Tsjw < TSJW_MIN_VAL) continue;

                        if (Tbs1 > TBS1_MAX_VAL) continue;
                        if (Tbs1 < TBS1_MIN_VAL) continue;

                        if (Tbs2 > TBS2_MAX_VAL) continue;
                        if (Tbs2 < TBS2_MIN_VAL) continue;

                        UInt64 CiaClacVal = CALC_CIA_VAL(Tsjw, Tbs1, Tbs2);

                        //结果大于最大值，取下一个值
                        if (CiaClacVal > CIA_MAX_VAL) continue;
                        //结果小于最小值，取下一个值
                        if (CiaClacVal < CIA_MIN_VAL) continue;

                        if (Tsjw == 0) continue;
                        if (Tbs1 == 0) continue;
                        if (Tbs2 == 0) continue;
                        if (BRP == 0) continue;

                        CParam.Param[CParam.index].Tsjw = Tsjw;
                        CParam.Param[CParam.index].BS1 = Tbs1;
                        CParam.Param[CParam.index].BS2 = Tbs2;
                        CParam.Param[CParam.index].BRP = (uint)BRP;
                        CParam.Param[CParam.index].BaudErr = (uint)baudErr;
                        CParam.Param[CParam.index].CiaVal = (uint)CiaClacVal;
                        CParam.index++;
                    }
                }
            }

            if (CParam.index == 0)
            {
                //计算结果无效全传0
                //callbackFun(0, 0, 0, 0, 0);
                return result;
            }

            //排序
            //for (int i = 0; i < GROUP_NUM; i++)
            int i = 0;
            {
                //for (int index = i + 1; index < (GROUP_NUM - 1); index++)
                for (int index = i + 1; index < (CParam.index - 1); index++)
                {
                    if ((CParam.Param[i].BaudErr > CParam.Param[index].BaudErr)
                        && (CParam.Param[index].Tsjw > 0) && (CParam.Param[index].BS1 > 0) && (CParam.Param[index].BS2 > 0) && (CParam.Param[index].BRP > 0))  //波特率误差小的，排在最前
                    {
                        temp.Tsjw = CParam.Param[i].Tsjw;
                        temp.BaudErr = CParam.Param[i].BaudErr;
                        temp.BRP = CParam.Param[i].BRP;
                        temp.BS1 = CParam.Param[i].BS1;
                        temp.BS2 = CParam.Param[i].BS2;
                        //temp.CiaErr = CParam.Param[i].CiaErr;
                        temp.CiaVal = CParam.Param[i].CiaVal;

                        CParam.Param[i].Tsjw = CParam.Param[index].Tsjw;
                        CParam.Param[i].BaudErr = CParam.Param[index].BaudErr;
                        CParam.Param[i].BRP = CParam.Param[index].BRP;
                        CParam.Param[i].BS1 = CParam.Param[index].BS1;
                        CParam.Param[i].BS2 = CParam.Param[index].BS2;
                        //CParam.Param[i].CiaErr = CParam.Param[index].CiaErr;
                        CParam.Param[i].CiaVal = CParam.Param[index].CiaVal;

                        CParam.Param[index].Tsjw = temp.Tsjw;
                        CParam.Param[index].BaudErr = temp.BaudErr;
                        CParam.Param[index].BRP = temp.BRP;
                        CParam.Param[index].BS1 = temp.BS1;
                        CParam.Param[index].BS2 = temp.BS2;
                        //CParam.Param[index].CiaErr = temp.CiaErr;
                        CParam.Param[index].CiaVal = temp.CiaVal;
                    }
                }
            }

            //将计算结果传出
            /*callbackFun(CParam.Param[0].Tsjw, 
                        CParam.Param[0].BS1, 
                        CParam.Param[0].BS2, 
                        CParam.Param[0].BRP, 
                        CParam.Param[0].CiaVal);*/

            //CParam.Param[0].Tsjw 	-> 1-4  		3 bit
            //CParam.Param[0].BS1  	-> 1-16 		5 bit
            //CParam.Param[0].BS2  	-> 1-8  		4 bit
            //CParam.Param[0].BRP  	-> 1-1024	11bit

            result |= (CParam.Param[0].Tsjw);
            result <<= 5;
            result |= (CParam.Param[0].BS1);
            result <<= 4;
            result |= (CParam.Param[0].BS2);
            result <<= 11;
            result |= (CParam.Param[0].BRP);

            return result;
        }
    }


    
}
